package com.xxjqr.dispatchserver.consumer;

import com.alibaba.fastjson.JSON;
import com.rabbitmq.client.Channel;
import com.xxjqr.dispatchserver.po.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

// 如果@RabbitListener注解放在类上，那么接收消息的方法就需要加上 @RabbitHandler注解
// 如果@RabbitListener放在方法上，就不需要
@RabbitListener(queues = "order_queue")
@Component
public class OrderConsumer {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //解决消息重试的几种方案
    //1：控制重试次数+进入死信队列
    //2：try+catch+手动ack
    //3：try+catch+手动ack+死信队列
    //注意：
    //1：如果使用了手动ack，那么重试机制将自动失效
    //2：如果消费队列绑定了死信队列，那么返回nack后消息自动移到死信队列
    @RabbitHandler
    @Transactional(rollbackFor = Exception.class)
    public void receiveMessage(String message, Channel channel,
                               CorrelationData correlationData,
                               @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        System.out.println("开始消费了");
        try {
            Order order = JSON.parseObject(message, Order.class);
            String sql = "insert into t_dispatch values (?,?,?,?);";
            final int effectCount = jdbcTemplate.update(sql, (int) (Math.random() * 100), order.getId(), "超人", 0);
            if (effectCount > 0) {
                System.out.println("插入成功:" + order.toString());
            }
            System.out.println(1/0);
            //参数：消息标识，是否所有消费者都收到
            // 这里是正常的回复mq，告诉它我收到了，不用重发了
            channel.basicAck(tag,false);
        } catch (Exception e) {
            System.out.println("出现错误，消息即将进入死信队列");
            // 返回not ack，告诉其我消费失败了；返回nack后消息将进入死信队列
            channel.basicNack(tag,false,false);
            //把异常抛出，这样@Transactional就能生效，异常之前的插入、修改都能回滚
            throw e;
        }
    }
}